Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Insert Links by default in Navigation block #34899

Merged
merged 11 commits into from
Sep 30, 2021

Conversation

tellthemachines
Copy link
Contributor

@tellthemachines tellthemachines commented Sep 17, 2021

Description

First steps to address #34041:

Changes Navigation block appender to insert Navigation Links by default, unless there are other types of blocks in the Navigation block already.

This is a rough draft so we can have a play and see how it feels.

One thing I'm not sure about is how it should behave when we "Add all pages". Due to the Page List being a different block type, should we show the quick inserter? Or should we treat Page List as if it were just a bunch of Navigation Links?

The other question is how to address submenu: when a Submenu block is added, the quick inserter will appear because Submenus are a different block type. But because they are often part of simple navs, should we consider them the same as Navigation Links?

Likewise, it might be good to insert links by default inside submenus too.

Updated to insert links by default when submenus are present (and inside submenus).

How has this been tested?

  • Add a Navigation block, choose "Start Empty" and click the plus button. A Navigation Link block should be inserted straightway.
  • From the top inserter, insert a different type of block, e.g. Search. Clicking the plus button should now show the quick inserter.
  • In the Navigation screen, check that Navigation Link blocks are inserted straightway.

Screenshots

Types of changes

New feature (non-breaking change which adds functionality)

This is more of an enhancement than a new feature, but 🤷

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • I've tested my changes with keyboard and screen readers.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR (please manually search all *.native.js files for terms that need renaming or removal).

@github-actions
Copy link

github-actions bot commented Sep 17, 2021

Size Change: +216 B (0%)

Total Size: 1.07 MB

Filename Size Change
build/block-editor/index.min.js 134 kB +172 B (0%)
build/block-library/index.min.js 147 kB +44 B (0%)
ℹ️ View Unchanged
Filename Size
build/a11y/index.min.js 931 B
build/admin-manifest/index.min.js 1.09 kB
build/annotations/index.min.js 2.7 kB
build/api-fetch/index.min.js 2.21 kB
build/autop/index.min.js 2.08 kB
build/blob/index.min.js 459 B
build/block-directory/index.min.js 6.19 kB
build/block-directory/style-rtl.css 1.01 kB
build/block-directory/style.css 1.01 kB
build/block-editor/default-editor-styles-rtl.css 378 B
build/block-editor/default-editor-styles.css 378 B
build/block-editor/style-rtl.css 13.9 kB
build/block-editor/style.css 13.9 kB
build/block-library/blocks/archives/editor-rtl.css 61 B
build/block-library/blocks/archives/editor.css 60 B
build/block-library/blocks/archives/style-rtl.css 65 B
build/block-library/blocks/archives/style.css 65 B
build/block-library/blocks/audio/editor-rtl.css 58 B
build/block-library/blocks/audio/editor.css 58 B
build/block-library/blocks/audio/style-rtl.css 111 B
build/block-library/blocks/audio/style.css 111 B
build/block-library/blocks/audio/theme-rtl.css 125 B
build/block-library/blocks/audio/theme.css 125 B
build/block-library/blocks/block/editor-rtl.css 161 B
build/block-library/blocks/block/editor.css 161 B
build/block-library/blocks/button/editor-rtl.css 474 B
build/block-library/blocks/button/editor.css 474 B
build/block-library/blocks/button/style-rtl.css 600 B
build/block-library/blocks/button/style.css 600 B
build/block-library/blocks/buttons/editor-rtl.css 315 B
build/block-library/blocks/buttons/editor.css 315 B
build/block-library/blocks/buttons/style-rtl.css 370 B
build/block-library/blocks/buttons/style.css 370 B
build/block-library/blocks/calendar/style-rtl.css 207 B
build/block-library/blocks/calendar/style.css 207 B
build/block-library/blocks/categories/editor-rtl.css 84 B
build/block-library/blocks/categories/editor.css 83 B
build/block-library/blocks/categories/style-rtl.css 79 B
build/block-library/blocks/categories/style.css 79 B
build/block-library/blocks/code/style-rtl.css 90 B
build/block-library/blocks/code/style.css 90 B
build/block-library/blocks/code/theme-rtl.css 131 B
build/block-library/blocks/code/theme.css 131 B
build/block-library/blocks/columns/editor-rtl.css 206 B
build/block-library/blocks/columns/editor.css 205 B
build/block-library/blocks/columns/style-rtl.css 497 B
build/block-library/blocks/columns/style.css 496 B
build/block-library/blocks/cover/editor-rtl.css 666 B
build/block-library/blocks/cover/editor.css 670 B
build/block-library/blocks/cover/style-rtl.css 1.23 kB
build/block-library/blocks/cover/style.css 1.23 kB
build/block-library/blocks/embed/editor-rtl.css 488 B
build/block-library/blocks/embed/editor.css 488 B
build/block-library/blocks/embed/style-rtl.css 417 B
build/block-library/blocks/embed/style.css 417 B
build/block-library/blocks/embed/theme-rtl.css 124 B
build/block-library/blocks/embed/theme.css 124 B
build/block-library/blocks/file/editor-rtl.css 300 B
build/block-library/blocks/file/editor.css 300 B
build/block-library/blocks/file/style-rtl.css 255 B
build/block-library/blocks/file/style.css 255 B
build/block-library/blocks/file/view.min.js 322 B
build/block-library/blocks/freeform/editor-rtl.css 2.44 kB
build/block-library/blocks/freeform/editor.css 2.44 kB
build/block-library/blocks/gallery/editor-rtl.css 977 B
build/block-library/blocks/gallery/editor.css 982 B
build/block-library/blocks/gallery/style-rtl.css 1.6 kB
build/block-library/blocks/gallery/style.css 1.59 kB
build/block-library/blocks/gallery/theme-rtl.css 122 B
build/block-library/blocks/gallery/theme.css 122 B
build/block-library/blocks/group/editor-rtl.css 159 B
build/block-library/blocks/group/editor.css 159 B
build/block-library/blocks/group/style-rtl.css 57 B
build/block-library/blocks/group/style.css 57 B
build/block-library/blocks/group/theme-rtl.css 78 B
build/block-library/blocks/group/theme.css 78 B
build/block-library/blocks/heading/style-rtl.css 114 B
build/block-library/blocks/heading/style.css 114 B
build/block-library/blocks/home-link/style-rtl.css 247 B
build/block-library/blocks/home-link/style.css 247 B
build/block-library/blocks/html/editor-rtl.css 332 B
build/block-library/blocks/html/editor.css 333 B
build/block-library/blocks/image/editor-rtl.css 728 B
build/block-library/blocks/image/editor.css 728 B
build/block-library/blocks/image/style-rtl.css 482 B
build/block-library/blocks/image/style.css 487 B
build/block-library/blocks/image/theme-rtl.css 124 B
build/block-library/blocks/image/theme.css 124 B
build/block-library/blocks/latest-comments/style-rtl.css 284 B
build/block-library/blocks/latest-comments/style.css 284 B
build/block-library/blocks/latest-posts/editor-rtl.css 137 B
build/block-library/blocks/latest-posts/editor.css 137 B
build/block-library/blocks/latest-posts/style-rtl.css 528 B
build/block-library/blocks/latest-posts/style.css 527 B
build/block-library/blocks/list/style-rtl.css 94 B
build/block-library/blocks/list/style.css 94 B
build/block-library/blocks/media-text/editor-rtl.css 266 B
build/block-library/blocks/media-text/editor.css 263 B
build/block-library/blocks/media-text/style-rtl.css 488 B
build/block-library/blocks/media-text/style.css 485 B
build/block-library/blocks/more/editor-rtl.css 431 B
build/block-library/blocks/more/editor.css 431 B
build/block-library/blocks/navigation-link/editor-rtl.css 568 B
build/block-library/blocks/navigation-link/editor.css 570 B
build/block-library/blocks/navigation-link/style-rtl.css 94 B
build/block-library/blocks/navigation-link/style.css 94 B
build/block-library/blocks/navigation-submenu/editor-rtl.css 300 B
build/block-library/blocks/navigation-submenu/editor.css 299 B
build/block-library/blocks/navigation-submenu/style-rtl.css 195 B
build/block-library/blocks/navigation-submenu/style.css 195 B
build/block-library/blocks/navigation-submenu/view.min.js 343 B
build/block-library/blocks/navigation/editor-rtl.css 1.72 kB
build/block-library/blocks/navigation/editor.css 1.72 kB
build/block-library/blocks/navigation/style-rtl.css 1.62 kB
build/block-library/blocks/navigation/style.css 1.61 kB
build/block-library/blocks/navigation/view.min.js 2.74 kB
build/block-library/blocks/nextpage/editor-rtl.css 395 B
build/block-library/blocks/nextpage/editor.css 395 B
build/block-library/blocks/page-list/editor-rtl.css 377 B
build/block-library/blocks/page-list/editor.css 377 B
build/block-library/blocks/page-list/style-rtl.css 198 B
build/block-library/blocks/page-list/style.css 198 B
build/block-library/blocks/paragraph/editor-rtl.css 157 B
build/block-library/blocks/paragraph/editor.css 157 B
build/block-library/blocks/paragraph/style-rtl.css 273 B
build/block-library/blocks/paragraph/style.css 273 B
build/block-library/blocks/post-author/editor-rtl.css 210 B
build/block-library/blocks/post-author/editor.css 210 B
build/block-library/blocks/post-author/style-rtl.css 182 B
build/block-library/blocks/post-author/style.css 181 B
build/block-library/blocks/post-comments-form/style-rtl.css 140 B
build/block-library/blocks/post-comments-form/style.css 140 B
build/block-library/blocks/post-comments/style-rtl.css 360 B
build/block-library/blocks/post-comments/style.css 359 B
build/block-library/blocks/post-content/editor-rtl.css 138 B
build/block-library/blocks/post-content/editor.css 138 B
build/block-library/blocks/post-excerpt/editor-rtl.css 73 B
build/block-library/blocks/post-excerpt/editor.css 73 B
build/block-library/blocks/post-excerpt/style-rtl.css 69 B
build/block-library/blocks/post-excerpt/style.css 69 B
build/block-library/blocks/post-featured-image/editor-rtl.css 398 B
build/block-library/blocks/post-featured-image/editor.css 398 B
build/block-library/blocks/post-featured-image/style-rtl.css 143 B
build/block-library/blocks/post-featured-image/style.css 143 B
build/block-library/blocks/post-template/editor-rtl.css 99 B
build/block-library/blocks/post-template/editor.css 98 B
build/block-library/blocks/post-template/style-rtl.css 391 B
build/block-library/blocks/post-template/style.css 392 B
build/block-library/blocks/post-terms/style-rtl.css 73 B
build/block-library/blocks/post-terms/style.css 73 B
build/block-library/blocks/post-title/style-rtl.css 60 B
build/block-library/blocks/post-title/style.css 60 B
build/block-library/blocks/preformatted/style-rtl.css 103 B
build/block-library/blocks/preformatted/style.css 103 B
build/block-library/blocks/pullquote/editor-rtl.css 198 B
build/block-library/blocks/pullquote/editor.css 198 B
build/block-library/blocks/pullquote/style-rtl.css 378 B
build/block-library/blocks/pullquote/style.css 378 B
build/block-library/blocks/pullquote/theme-rtl.css 167 B
build/block-library/blocks/pullquote/theme.css 167 B
build/block-library/blocks/query-pagination-numbers/editor-rtl.css 122 B
build/block-library/blocks/query-pagination-numbers/editor.css 121 B
build/block-library/blocks/query-pagination/editor-rtl.css 262 B
build/block-library/blocks/query-pagination/editor.css 255 B
build/block-library/blocks/query-pagination/style-rtl.css 234 B
build/block-library/blocks/query-pagination/style.css 231 B
build/block-library/blocks/query-title/editor-rtl.css 85 B
build/block-library/blocks/query-title/editor.css 85 B
build/block-library/blocks/query/editor-rtl.css 131 B
build/block-library/blocks/query/editor.css 132 B
build/block-library/blocks/quote/style-rtl.css 187 B
build/block-library/blocks/quote/style.css 187 B
build/block-library/blocks/quote/theme-rtl.css 220 B
build/block-library/blocks/quote/theme.css 222 B
build/block-library/blocks/rss/editor-rtl.css 202 B
build/block-library/blocks/rss/editor.css 204 B
build/block-library/blocks/rss/style-rtl.css 289 B
build/block-library/blocks/rss/style.css 288 B
build/block-library/blocks/search/editor-rtl.css 165 B
build/block-library/blocks/search/editor.css 165 B
build/block-library/blocks/search/style-rtl.css 374 B
build/block-library/blocks/search/style.css 375 B
build/block-library/blocks/search/theme-rtl.css 64 B
build/block-library/blocks/search/theme.css 64 B
build/block-library/blocks/separator/editor-rtl.css 99 B
build/block-library/blocks/separator/editor.css 99 B
build/block-library/blocks/separator/style-rtl.css 250 B
build/block-library/blocks/separator/style.css 250 B
build/block-library/blocks/separator/theme-rtl.css 172 B
build/block-library/blocks/separator/theme.css 172 B
build/block-library/blocks/shortcode/editor-rtl.css 474 B
build/block-library/blocks/shortcode/editor.css 474 B
build/block-library/blocks/site-logo/editor-rtl.css 462 B
build/block-library/blocks/site-logo/editor.css 464 B
build/block-library/blocks/site-logo/style-rtl.css 153 B
build/block-library/blocks/site-logo/style.css 153 B
build/block-library/blocks/site-tagline/editor-rtl.css 86 B
build/block-library/blocks/site-tagline/editor.css 86 B
build/block-library/blocks/site-title/editor-rtl.css 84 B
build/block-library/blocks/site-title/editor.css 84 B
build/block-library/blocks/social-link/editor-rtl.css 165 B
build/block-library/blocks/social-link/editor.css 165 B
build/block-library/blocks/social-links/editor-rtl.css 812 B
build/block-library/blocks/social-links/editor.css 811 B
build/block-library/blocks/social-links/style-rtl.css 1.3 kB
build/block-library/blocks/social-links/style.css 1.3 kB
build/block-library/blocks/spacer/editor-rtl.css 307 B
build/block-library/blocks/spacer/editor.css 307 B
build/block-library/blocks/spacer/style-rtl.css 48 B
build/block-library/blocks/spacer/style.css 48 B
build/block-library/blocks/table/editor-rtl.css 471 B
build/block-library/blocks/table/editor.css 472 B
build/block-library/blocks/table/style-rtl.css 481 B
build/block-library/blocks/table/style.css 481 B
build/block-library/blocks/table/theme-rtl.css 188 B
build/block-library/blocks/table/theme.css 188 B
build/block-library/blocks/tag-cloud/style-rtl.css 146 B
build/block-library/blocks/tag-cloud/style.css 146 B
build/block-library/blocks/template-part/editor-rtl.css 636 B
build/block-library/blocks/template-part/editor.css 635 B
build/block-library/blocks/template-part/theme-rtl.css 101 B
build/block-library/blocks/template-part/theme.css 101 B
build/block-library/blocks/term-description/editor-rtl.css 90 B
build/block-library/blocks/term-description/editor.css 90 B
build/block-library/blocks/text-columns/editor-rtl.css 95 B
build/block-library/blocks/text-columns/editor.css 95 B
build/block-library/blocks/text-columns/style-rtl.css 166 B
build/block-library/blocks/text-columns/style.css 166 B
build/block-library/blocks/verse/style-rtl.css 87 B
build/block-library/blocks/verse/style.css 87 B
build/block-library/blocks/video/editor-rtl.css 571 B
build/block-library/blocks/video/editor.css 572 B
build/block-library/blocks/video/style-rtl.css 173 B
build/block-library/blocks/video/style.css 173 B
build/block-library/blocks/video/theme-rtl.css 124 B
build/block-library/blocks/video/theme.css 124 B
build/block-library/common-rtl.css 853 B
build/block-library/common.css 849 B
build/block-library/editor-rtl.css 9.76 kB
build/block-library/editor.css 9.75 kB
build/block-library/reset-rtl.css 536 B
build/block-library/reset.css 536 B
build/block-library/style-rtl.css 10.4 kB
build/block-library/style.css 10.4 kB
build/block-library/theme-rtl.css 665 B
build/block-library/theme.css 669 B
build/block-serialization-default-parser/index.min.js 1.09 kB
build/block-serialization-spec-parser/index.min.js 2.79 kB
build/blocks/index.min.js 45.7 kB
build/components/index.min.js 214 kB
build/components/style-rtl.css 15.9 kB
build/components/style.css 15.9 kB
build/compose/index.min.js 10.3 kB
build/core-data/index.min.js 12.3 kB
build/customize-widgets/index.min.js 11.1 kB
build/customize-widgets/style-rtl.css 1.5 kB
build/customize-widgets/style.css 1.49 kB
build/data-controls/index.min.js 614 B
build/data/index.min.js 7.1 kB
build/date/index.min.js 31.5 kB
build/deprecated/index.min.js 428 B
build/dom-ready/index.min.js 304 B
build/dom/index.min.js 4.45 kB
build/edit-navigation/index.min.js 15.3 kB
build/edit-navigation/style-rtl.css 3.71 kB
build/edit-navigation/style.css 3.7 kB
build/edit-post/classic-rtl.css 492 B
build/edit-post/classic.css 494 B
build/edit-post/index.min.js 29.2 kB
build/edit-post/style-rtl.css 7.19 kB
build/edit-post/style.css 7.18 kB
build/edit-site/index.min.js 28.4 kB
build/edit-site/style-rtl.css 5.4 kB
build/edit-site/style.css 5.39 kB
build/edit-widgets/index.min.js 15.7 kB
build/edit-widgets/style-rtl.css 4.1 kB
build/edit-widgets/style.css 4.1 kB
build/editor/index.min.js 37.4 kB
build/editor/style-rtl.css 3.76 kB
build/editor/style.css 3.75 kB
build/element/index.min.js 3.17 kB
build/escape-html/index.min.js 517 B
build/format-library/index.min.js 5.68 kB
build/format-library/style-rtl.css 571 B
build/format-library/style.css 571 B
build/hooks/index.min.js 1.55 kB
build/html-entities/index.min.js 424 B
build/i18n/index.min.js 3.6 kB
build/is-shallow-equal/index.min.js 501 B
build/keyboard-shortcuts/index.min.js 1.72 kB
build/keycodes/index.min.js 1.3 kB
build/list-reusable-blocks/index.min.js 1.85 kB
build/list-reusable-blocks/style-rtl.css 838 B
build/list-reusable-blocks/style.css 838 B
build/media-utils/index.min.js 2.92 kB
build/notices/index.min.js 845 B
build/nux/index.min.js 2.03 kB
build/nux/style-rtl.css 747 B
build/nux/style.css 743 B
build/plugins/index.min.js 1.83 kB
build/primitives/index.min.js 921 B
build/priority-queue/index.min.js 582 B
build/react-i18n/index.min.js 671 B
build/redux-routine/index.min.js 2.63 kB
build/reusable-blocks/index.min.js 2.28 kB
build/reusable-blocks/style-rtl.css 256 B
build/reusable-blocks/style.css 256 B
build/rich-text/index.min.js 10.6 kB
build/server-side-render/index.min.js 1.5 kB
build/shortcode/index.min.js 1.48 kB
build/token-list/index.min.js 562 B
build/url/index.min.js 1.74 kB
build/viewport/index.min.js 1.02 kB
build/warning/index.min.js 248 B
build/widgets/index.min.js 7.11 kB
build/widgets/style-rtl.css 1.16 kB
build/widgets/style.css 1.16 kB
build/wordcount/index.min.js 1.04 kB

compressed-size-action

@jasmussen
Copy link
Contributor

Your stamina to take one difficult ticket after another is impressive. Thank you. It's already looking good:

nav

As is, this feels very validating towards the concept as a whole. Working towards the second half, transforms, it's already becoming clear that showing the 3 most recent items published in the page selector, requiring search for anything else, isn't that useful. This is almost certainly not something that should be solved in this PR, and there are some mockups in this ticket, but it feels like a worthwhile next step.

Let me know if/how I can help with this one.

@gwwar
Copy link
Contributor

gwwar commented Sep 20, 2021

Thanks for looking into this @tellthemachines ! I gave this a try, and one thing I quite like about how the buttons container works currently is that it quickly lets us add our placeholders before we need to fill in the details.

In the context of the navigation editor, this branch lets us add items pretty quickly, but I got pretty distracted by the link search that auto opens. If we're trying to quickly build a submenu, this makes it a little more apparent that it can get in the way if I'm building a menu in that way.

If we prevent the link search from auto opening, a natural thing to do might also be to add an option to the link search to open the quick inserter if we're not interested in a link:

CleanShot.2021-09-20.at.13.17.23.mp4

Note that if this PR ends up being viable, we can also use the method here to improve how social icons inserts items in a follow up✨

@gwwar
Copy link
Contributor

gwwar commented Sep 20, 2021

Let's try making it insert a Page Item by default

If we want page links, we'll need to add the following attributes to make sure we get the Select Page prompt:

<!-- wp:navigation-link {"type":"page","kind":"post-type" } /-->

CleanShot 2021-09-20 at 13 46 31@2x

@tellthemachines
Copy link
Contributor Author

Let's try making it insert a Page Item by default

If we want page links, we'll need to add the following attributes to make sure we get the Select Page prompt:

Updated to Page items by default, but I now realise that this complicates the flow for inserting other types of links: searching for e.g a category name won't show any results. In optimising for Pages we risk making the general experience worse instead of better 😕
I'd love some more thoughts on this!

In the context of the navigation editor, this branch lets us add items pretty quickly, but I got pretty distracted by the link search that auto opens. If we're trying to quickly build a submenu, this makes it a little more apparent that it can get in the way if I'm building a menu in that way.

The link control auto-opening is what happens currently when adding a Link block, so changing that isn't in scope for this PR. In any case, I'm not convinced we should change it. Nav links are different to buttons in that they are most often based on existing pages or posts, so it doesn't make sense to add a text placeholder for them and go back later to add the actual link. That would just be twice the amount of work 😅

I'm going to mark this one ready for review, but I'll hold off on updating the e2e tests until I have some more feedback on Page items by default, because going with that will force us to change quite a few things.

() => ( onlyLinkInnerBlocks ? DIRECT_INSERT : [] ),
[ onlyLinkInnerBlocks ]
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is memoized to stop the directInsertValue array from getting recreated on every render, and causing an infinite loop because it changes innerBlocks, which in turn the Navigation function depends on to calculate the directInsertValue 😖

I can't think of a better way to do it though. I think this logic should reside in the block, because it might vary a lot - Submenus are set to always use it (at least until we sort out mega menus), and conceivably there could be blocks using it depending on their nesting status or some other condition.

@talldan talldan requested a review from mtias September 21, 2021 06:00
@talldan
Copy link
Contributor

talldan commented Sep 21, 2021

I have mixed feelings. Inserting pages is fast and easy, and is probably what most users will want to do, so that's great.

But it also feels like the other options are very hidden and can only be added in a very indirect way. There are probably ways to mitigate this. Perhaps by improving the Link UI (I think @jasmussen did some explorations) or I think @mtias mentioned making the slash inserter usable (though you can't just type into a Page block, a link has to be selected first) alongside other ideas in the issue - #34041.

@@ -504,6 +509,7 @@ export default function NavigationSubmenuEdit( {
},
{
allowedBlocks: ALLOWED_BLOCKS,
directInsert: DIRECT_INSERT,
Copy link
Contributor

@talldan talldan Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing mechanisms in variations and blocks to define whether they can be used in the inserter. Introducing some level of granularity there might be an alternative to this prop.

e.g.

"supports": {
    "inserter": "__experimentalQuick" | "__experimentalGlobal" | true | false
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember @gwwar also looked into a default block for the navigation block, so going back to that might also be an option.

Copy link
Contributor

@gwwar gwwar Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The related PR was here, where we explored adding a default block per container (vs one for the entire editor). #29490.

If I recall we ran into some of the same issues, where we made a guess as to what folks wanted to insert, but transforming blocks after that was more difficult.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's highlight the default block better in the quick inserter.

🤔 From #34041 it might be worth trying an __experimentalDefaultBlock block list setting again.

I'll try tidying that experimental branch to see if its viable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing mechanisms in variations and blocks to define whether they can be used in the inserter. Introducing some level of granularity there might be an alternative to this prop.

I don't think a setting in the block.json will work in this case, because we need to change it based on the block's innerBlocks. If any of the innerBlocks are not Nav Links, the quick inserter should appear instead of directly inserting a Link.

For the same reason, I'm not sure we'd want to leverage @gwwar 's __experimentalDefaultBlock, because the Insert before and Insert after buttons would be tied to its value. If we use __experimentalDefaultBlock, as it stands in #29490, to tell the block inserter when we want a default block to be inserted directly, we'll end up disabling Insert before and Insert after whenever the Navigation contains anything other than Links, which is quite unexpected.

Independently of whether we do enable Insert before and Insert after on all block containers in #29490 (which I think we should! it makes total sense) we still want to make it easier to insert Nav Links inside the Nav block, and a workflow that forces us to look for a deeply hidden option in the block toolbar doesn't satisfy that requirement.

In order to keep useInnerBlocksProps simple it would be nice if we could leverage the same prop for both use cases, but I'm not sure it will be possible. Am keen to know if anyone has other ideas around this though!

Copy link
Contributor

@talldan talldan Sep 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That part of it is pretty complicated. There's still this part of the issue that indicates a way to designate a default block within the nav block will be needed:

Let's highlight the default block better in the quick inserter. It should always come in first. A user should not have to think when they open a quick inserter if they are not looking for a specific block.

I don't know that the current directInsert prop will work well with that.

Maybe an option is to look at using renderAppender to build out a lot of the custom insertion behavior first before formalising it into props and configuration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if __experimentalDefaultBlock is viable, but the idea of a default block other than a global one for the editor may still be useful. (The buttons block inserter cheats a bit since only one child item is allowed and doesn't apply well to other situations).

I mostly just rebased the branch yesterday and resolved conflicts. I'll ping y'all if I think there might be viable flow to look at.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe an option is to look at using renderAppender to build out a lot of the custom insertion behavior first before formalising it into props and configuration.

Not sure if I'm missing something, but renderAppender seems to be used only for determining what the appender looks like. It doesn't feel right to mix that with its behaviour (whether it should display quick inserter or insert a block directly), because we'll want to be able to set both independently.

There's still this part of the issue that indicates a way to designate a default block within the nav block will be needed

This is a good point. I'm thinking we could have two props: a defaultBlock array that specifies the default block name and any relevant attributes, and a directInsert boolean that specifies whether the default block, if it exists, should be inserted directly or not. That way defaultBlock can be used to specify the default for the quick inserter, as well as for insert before and after. If I'm not mistaken defaultBlock will need to be passed to useInnerBlockProps for all of these scenarios.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gwwar I'm going to steal the__experimentalDefaultBlock prop and give it a go in this PR 😂

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Go for it ✨

@getdave
Copy link
Contributor

getdave commented Sep 21, 2021

Perhaps by improving the Link UI (I think @jasmussen did some explorations)

We're looking at this in #33849 (comment).

Copy link
Contributor

@getdave getdave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for working on this.

I recognise the need for a lighter and faster experience for adding links.

My instinct is that this may not be the right solution. It makes adding Pages fast but it makes adding any other kind of link variation far more obscure.

One option we may not have considered is enabling some kind of "bulk addition" UI as a complementary alternative to the inline inserter. We're actually looking into this at the moment in the Navigation Editor and I wonder whether perhaps this ability to insert any type of item in bulk without triggering the Link UI (which I'm happy to work on or make configurable btw!) would also benefit the block?

I also think it would be worth exploring the scenario where:

  • clicking plus auto-inserts a core/navigation link block without triggering the Link UI.
  • you can repeatedly add navigation link items without multiple clicks to close the link UI.
  • once you're done with the "adding items" bit you can click on any item to "configure it" (we could show a visual indicator this is required).

I think this is what @gwwar suggested above. That would be a nicer flow in my opinion. It would have two (conceptual) "modes":

  • insertion mode (I'm adding items).
  • configuration mode (I'm setting where the items link to).

Thanks for working on this. I'm going to go away now and look at home to make the auto-appearing link UI popup configurable. I did an experiment #35001

@draganescu
Copy link
Contributor

I like this idea suggested by Dave

  • clicking plus auto-inserts a core/navigation link block without triggering the Link UI.
  • you can repeatedly add navigation link items without multiple clicks to close the link UI.
  • once you're done with the "adding items" bit you can click on any item to "configure it" (we could show a visual indicator this is required).

@priethor
Copy link
Contributor

once you're done with the "adding items" bit you can click on any item to "configure it" (we could show a visual indicator this is required).

As I commented in #35001 (comment), I feel switching the insertion flow and asking users to first add all links, then set them, is a hard ask and not the smoothest insertion flow.

If we want to go for a bulk-add hybrid approach instead, how about we explore adding a different appender to "insert another" of the same kind? This would allow, for example, inserting successive page links right after adding a first one, so that creating simple link menus is very straightforward but without obscuring the ability to add other types of blocks.

@adamziel
Copy link
Contributor

adamziel commented Sep 21, 2021

The way I understand the discussion is this:

  • Inserting links without asking whether that's a page, post, tags, category etc feels smooth – that's great
  • But if we insert a page link, it hides the posts, tags, and categories links from the user – that's bad

Can we eat a cake and have it too? Imagine a link UI that shows anything that matches: pages, posts, tags, categories, you name it:

Zrzut ekranu 2021-09-21 o 17 09 38

Once you choose a tag, it becomes a tag link. If you edit it, it could still show all results for consistent experience – this way you could easily change it into a page link. Would that work, or am I missing something here?

@talldan
Copy link
Contributor

talldan commented Sep 22, 2021

I second @adamziel's suggestion, but with a slight twist.

I think it could work by:

  • Don't show navigation link variations (page, post, tag) in the quick inserter, just the base 'Link' block. The link block is inserted by default when clicking +, a bit like we're exploring here. (Reasoning: the quick inserter and main inserter are overwhelming with too many options, particularly when a site has lots of custom post types or taxonomies.)
  • The URL picker shows pages by default for the link block, but also has a way to switch to other types or all types. (Reasoning: pages are what a user will usually want to insert)
  • The selected link type determines which variation is used (this part should already work, you can see it by selecting a Custom Link, and then choosing a Page).

Copy link
Contributor

@gwwar gwwar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good so far @tellthemachines! 💖 The interactions feel good to me in both the post and navigation editor.

What we need next is updated tests and maybe a few more eyes on the new API. I left a few suggestions, but I'm curious what others think too.

directInsertBlock[ 0 ],
directInsertBlock[ 1 ]
)
: createBlock( allowedBlockType.name );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the default block can work as an array or object. It may help to give a few more hints on the data structure with something like:

const [ name, attributes, innerBlocks ] = defaultBlock;
createBlock( name, attributes, innerBlocks );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you think of a scenario where we'd need defaultBlock to be an object? In the existing use cases (both this and your other PR) we're using it to store values to pass to createBlock so it makes more sense as an array.

In this PR, it's defined as an array in the jsdocs for the parameters passed to useNestedSettingsUpdate and the return value of __experimentalGetDirectInsertBlock. We'd need to change those if we wanted to support an object too.

Copy link
Contributor

@adamziel adamziel Sep 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me, a good reason to make it an object would be passing around a block instead of an array of arguments for createBlock. For example, this API could look like const DEFAULT_BLOCK = createBlock( 'core/navigation-link' ). One thing I don't like there is creating a uuid and a few other things we don't really need.

Maybe that could be a function then? For example const DEFAULT_BLOCK_CREATOR = () => createBlock( 'core/navigation-link' )

Copy link
Contributor

@gwwar gwwar Sep 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Good point, I suppose folks will need to use createBlock for complex innerBlocks and we'll want to avoid keeping around a static uuid for blocks. A function would make sense in that case, though we might want to pick a friendlier name. (I find that some folks get confused by the creator name). Maybe INSERT_DEFAULT_BLOCK or something similar?

Edit: Maybe a simple name like getDefaultBlock? One other piece of this is we'll likely need to modify the suggested items in the quick inserter as part of 34041. (The quick inserter is currently ordered by block frecency).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not so sure about this. When allowedBlocks is in use, the block is created directly in the inserter. I'd prefer to emulate that logic: fetching a block type, and creating the block from that type in the inserter. Default block and allowed block shouldn't be too different, because they essentially serve the same purpose. The main difference is that only one default block may exist even if multiple blocks are allowed.

I suppose folks will need to use createBlock for complex innerBlocks and we'll want to avoid keeping around a static uuid for blocks

I'm not sure I follow. We shouldn't have any reason to pass innerBlocks to the inserter, because it should always insert a brand new block.

Copy link
Contributor

@adamziel adamziel Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose folks will need to use createBlock for complex innerBlocks and we'll want to avoid keeping around a static uuid for blocks

I think @gwwar referred to pre-populating that block with inner blocks. For example, that could be a pre-populated menu like this:

const DEFAULT_BLOCK_CREATOR = () => createBlock( 'core/navigation-menu', {}, [
    createBlock( 'core/navigation-link', { title: "Home" }, [ ] ),
    createBlock( 'core/navigation-link', { title: "About" }, [ ] ),
] )

But as you say, I don't think it's a use-case that exists today.

Default block and allowed block shouldn't be too different, because they essentially serve the same purpose.

Hm I just thought of a completely different perspective here. Could we just make it a string to point out which supported block do we want to insert by default? It must be one of the supported blocks and we don't specify any attributes at the moment so maybe we don't need anything more? Or are we running in circles now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think @gwwar referred to pre-populating that block with inner blocks. For example, that could be a pre-populated menu like this:

That's right. A better example might be a container block that makes little sense without children, for example the buttons block.

Since we don't have an explicit case for passing though attributes or innerBlocks yet, I think it's fine deferring needing to pass through extra arguments until we need them. Whichever combination you prefer @tellthemachines

Copy link
Contributor

@gwwar gwwar Sep 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const blockToInsert = directInsertBlock?.length
					? createBlock( ...directInsertBlock )
					: createBlock( allowedBlockType.name )

One thing to note is that if folks pass through innerBlocks as the third item in an array, it's possible to have innerBlocks with static uuids. It's an edge case, but two options I can think of:

  1. clone the block
insertBlock( cloneBlock( blockToInsert ), getInsertionIndex(), rootClientId );
  1. Avoid passing through innerBlocks if we don't want to handle this case yet
const [ name, attributes ] = directInsertBlock;
createBlock( name, attributes )

( block ) =>
block.name === 'core/navigation-link' ||
block.name === 'core/navigation-submenu'
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we considered allowing __experimentalDirectInsert to be a match function which returns true or false? It doesn't look like we need any other information specific to the render function. One benefit is that we can then make it a constant and can define it next to the default block.

For example it might look like:

const DIRECT_INSERT = ( block ) => { 
    return block.innerBlocks.every( ( { name } ) => name === 'core/navigation-link' || name === 'core/navigation-submenu' );
};

And in the selector we can update it to call the match function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oooh nice! I'll try it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this doesn't work well when we either add or remove other block types, because the selector won't re-run with every block update, so it won't know when the content has changed. Whereas if we keep the logic inside the edit function it updates with every change in the inner blocks.

Copy link
Contributor

@gwwar gwwar Sep 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried without createSelector? We should get proper results provided we recalculate whenever the block.innerBlocks reference updates.

Edit: I see some earlier comments about an infinite loop. I'll 🔍 a bit. This is more of a nice to have if we can define this outside.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The following works for me. (I didn't notice any infinite loops, but let me know if there's a specific case to test.)

Non-memo'd:

export const __experimentalGetDirectInsertBlock = (
	state,
	rootClientId = null
) => {
	if ( ! rootClientId ) {
		return;
	}
	const settings = state.blockListSettings[ rootClientId ];
	const defaultBlock = settings?.__experimentalDefaultBlock;
	if ( ! defaultBlock ) {
		return;
	}
	const directInsert = settings?.__experimentalDirectInsert(
		getBlock( state, rootClientId )
	);
	if ( ! directInsert ) {
		return;
	}

	return defaultBlock;
};

Or memo'd

export const __experimentalGetDirectInsertBlock = createSelector(
	( state, rootClientId = null ) => {
		if ( ! rootClientId ) {
			return;
		}
		const settings = state.blockListSettings[ rootClientId ];
		const defaultBlock = settings?.__experimentalDefaultBlock;
		if ( ! defaultBlock ) {
			return;
		}
		const directInsert = settings?.__experimentalDirectInsert(
			getBlock( state, rootClientId )
		);
		if ( ! directInsert ) {
			return;
		}

		return defaultBlock;
	},
	( state, rootClientId ) => [
		state.blockListSettings[ rootClientId ],
		state.blocks.tree[ rootClientId ],
	]
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, I was forgetting to add state.blocks.tree[ rootClientId ] to the dependencies 🤦

I think we should also accept a boolean value though, to avoid having to do something like () => true when we want to always insert the default block.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also accept a boolean value though

That sounds reasonable to me 👍

@tellthemachines
Copy link
Contributor Author

If I understand correctly, this makes it possible to only use direct insertion for some blocks lists, but not for the other? That's nice and could come handy when mixing different contexts in the same editor, or using the same block in different editors.

Direct insertion depends on the block for now, though conceivably we could change it to be dependent on context if necessary. Right now it isn't because we want the same behaviour in all the editors.

What we need next is updated tests and maybe a few more eyes on the new API.

Thanks for the review @gwwar ! I've updated the failing tests and added a new test to check the quick inserter still works when non-link blocks are present. Would welcome more feedback on the API!

Copy link
Contributor

@gwwar gwwar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @tellthemachines I think we're in pretty good shape here. This tests well for me manually and the test updates make sense. ✨

We may want a +1 review from folks working on the Navigation Editor since this effectively makes it pretty difficult to add a link variation. The only flow I found for that was using the global inserter.

Here's what I see. Note that the navigation crash at the end when hovering over a transform is probably from a different bug that has been fixed. (I'd recommend a rebase with trunk to verify).

CleanShot.2021-09-29.at.09.40.04.mp4

@tellthemachines
Copy link
Contributor Author

Note that the navigation crash at the end when hovering over a transform is probably from a different bug that has been fixed.

Yup, it's not happening after rebase.

Copy link
Contributor

@talldan talldan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for working on this.

@tellthemachines tellthemachines merged commit fe76803 into trunk Sep 30, 2021
@tellthemachines tellthemachines deleted the add/default-nav-appender branch September 30, 2021 04:37
@github-actions github-actions bot added this to the Gutenberg 11.7 milestone Sep 30, 2021
@mtias
Copy link
Member

mtias commented Sep 30, 2021

Nice! This heuristic is going to be pretty important going forwards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Block] Navigation Affects the Navigation Block [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants